import cv2 as cv
import os
from PIL import Image
import numpy as np
import time

def getFace_data():
    '''
    数据训练
    :return:
    '''
    faceSamples=[]
    names=[]

    path = './image/Register'
    imagePaths=[os.path.join(path,f) for f in os.listdir(path)]

    #加载特征数据创建人脸分类器
    face_frame = cv.CascadeClassifier(r'C:\Users\HJJ\AppData\Local\Programs\Python\Python39\Lib\site-packages\cv2\data\haarcascade_frontalface_default.xml')

    #遍历图片
    for i in imagePaths:
        #打开图片
        PIL_img = Image.open(i).convert('L')
        # 将图像转为数组
        img_numpy = np.array(PIL_img, 'uint8')
        faces = face_frame.detectMultiScale(img_numpy)  # 得到一个区域
        print(f"Detected {len(faces)} faces in image {i}")
        # 获取每张图片的姓名
        name = os.path.split(i)[1].split('.')[0]
        names.append(name)
        for x, y, w, h in faces:
            faceSamples.append(img_numpy[y:y + h, x:x + w])

    print(faceSamples,names)
    return faceSamples,names


def train_data():
    name_to_id_map = {}
    id_to_name_map = {}
    next_id = 0

    faces, names = getFace_data()
    # 构建名字到ID的映射
    for name in names:
        if name not in name_to_id_map:
            name_to_id_map[name] = next_id
            id_to_name_map[next_id] = name
            next_id += 1
    # 将名字替换为相应的ID
    labels = np.array([name_to_id_map[name] for name in names], dtype=np.int32)

    recognizer = cv.face.LBPHFaceRecognizer_create()
    recognizer.train(faces, labels)
    recognizer.write('./image/UserTranFace.yml')

    return id_to_name_map  # 返回ID到名字的映射

def my_Face_demo(id_to_name_map):
    '''
    自主学习的人脸案例
    :return:
    '''

    # 获取摄像头，传入0表示获取系统默认摄像头
    cap = cv.VideoCapture(0, cv.CAP_DSHOW)
    # 打开cap
    cap.open(0)



    # 循环
    while cap.isOpened():
        # 获取画面
        flag, frame = cap.read()  # flag返回得到的是一个布尔值true代表获取画面成功否则为false frame代表的是一个三维数组为摄像头获取的画面
        #对获取到的画面进行人脸比对
        if flag == True:
            gray = cv.cvtColor(frame,cv.COLOR_BGR2GRAY)

            #创建人脸比对分类器
            face_frame = cv.CascadeClassifier(r'C:\Users\HJJ\AppData\Local\Programs\Python\Python39\Lib\site-packages\cv2\data\haarcascade_frontalface_default.xml')
            face_frame.load(r'C:\Users\HJJ\AppData\Local\Programs\Python\Python39\Lib\site-packages\cv2\data\haarcascade_frontalface_default.xml')

            #调用人脸识别
            face_Res = face_frame.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=5,minSize=(35,35))
            cv.imwrite('./image/Login/faceNow.jpg', frame)
            for i in face_Res:
                #画出人脸对比结果
                x,y,w,h = i
                cv.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),3)
                #在矩阵中添加提示
                cv.putText(frame,'face', (x, y - 7), 3, 1.2, (255, 0, 0), 2, cv.LINE_AA)
            #显示当前识别的每一帧图片
            cv.imshow('frame',frame)


        # 获取键盘上的按键
        key_pressed = cv.waitKey(60)  # 每60毫秒监听一次  这里的单位是毫秒 键盘事件监听
        print('键盘上按下的键是：', key_pressed)  # 获取的数值代表键位对应的ASCLL码
        # 如果按下的是esc，就退出循环
        if key_pressed == 27:  # 27代表ESC键在ASCLL码中的数值
            break

        #加载训练模型数据
        recogizer = cv.face.LBPHFaceRecognizer_create()
        recogizer.read('./image/UserTranFace.yml')

        #加载当前识别图片
        try:
            faceNow = cv.imread('./image/Login/faceNow.png')
            gray = cv.cvtColor(faceNow, cv.COLOR_BGR2GRAY)  # 变灰
            # 加载特征数据
            face_detector = cv.CascadeClassifier(r'C:\Users\HJJ\AppData\Local\Programs\Python\Python39\Lib\site-packages\cv2\data\haarcascade_frontalface_default.xml')
            faces = face_detector.detectMultiScale(gray)  # 得到一个区域  图片2需要加上scaleFactor=1.44
            for x, y, w, h in faces:
                cv.rectangle(faceNow, (x, y), (x + w, y + h), (0, 255, 0), 2)
                # 人脸识别
                id, confidence = recogizer.predict(gray[y:y + h, x:x + w])
                name = id_to_name_map[id]
                print('用户名:', name, '评分:', confidence)  # 评分越低即越像
                if (confidence < 70):
                    print('人脸识别成功')
                    print([True,name])
                    return [True,name]
                else:
                    print('识别失败数据库中未录入')

        except:
            print('未收到来自摄像头的图片')



    # 关闭摄像头
    cap.release()
    # 关闭图像窗口
    cv.destroyAllWindows()

if __name__ == '__main__':
    #人脸模型训练
    id_to_name_map = train_data()
    #开启摄像头进行模型识别
    my_Face_demo(id_to_name_map)
